/*
 * Copyright (c) 2021 The red-star Project
 *
 * Licensed under the Apache License, version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.inyourcode.core.transport.session;

import com.inyourcode.core.util.Lists;
import com.inyourcode.core.transport.session.api.AsyncRequest;
import com.inyourcode.core.transport.session.api.FixedRequest;
import com.inyourcode.core.transport.session.api.MessageHolder;
import com.inyourcode.core.transport.session.api.NoAuthRequest;
import com.inyourcode.core.transport.session.api.RequestMapping;
import com.inyourcode.core.transport.session.api.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * 请求处理的映射关系
 * @author JackLei
 */
public class SessionHandlerMapping {
    private static final Logger LOGGER = LoggerFactory.getLogger(SessionHandlerMapping.class);
    private static final Set<Long> INVOKE_ID_SET = new HashSet<>();
    private static final Map<Long, HandlerWrapper> handlerMap = new HashMap<>();

    public static void mappingHandler(Collection<Object> handlerObjects) throws IllegalHandlerException{
        for (Object handlerObj : handlerObjects) {
            List<HandlerWrapper> mappingList = Lists.newArrayList();
            Class clazz = handlerObj.getClass();
            boolean annotationPresent = clazz.isAnnotationPresent(Controller.class);
            if (!annotationPresent) {
                continue;
            }

            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                boolean haveHandlerAnno = method.isAnnotationPresent(RequestMapping.class);
                if (!haveHandlerAnno) {
                    continue;
                }

                RequestMapping handlerAnno = method.getAnnotation(RequestMapping.class);
                Class messageClazz = handlerAnno.builder();
                Long invokeId = handlerAnno.invokeId();
                if (INVOKE_ID_SET.contains(invokeId)) {
                    throw new IllegalHandlerException(String.format("Invoke id is duplicate,method = %s,id = %d ", method, invokeId));
                }

                AsyncRequest asycRequest = method.getAnnotation(AsyncRequest.class);
                FixedRequest fixedRequest = method.getAnnotation(FixedRequest.class);
                NoAuthRequest noAuthRequest = method.getAnnotation(NoAuthRequest.class);

                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes == null || parameterTypes.length != 2) {
                    throw new IllegalHandlerException(String.format("The returned type value by this method -> %s must be -> %s", method, MessageHolder.class));
                }

                if (parameterTypes[0] != Session.class) {
                    throw new IllegalHandlerException(String.format("The first param value by this method -> %s must be ->%s", method, Session.class));
                }

                if (parameterTypes[1].getSuperclass() != MessageHolder.class) {
                    throw new IllegalHandlerException(String.format("The second param value by this method -> %s must be -> %s or %s", method, RequestContext.class, MessageHolder.class));
                }

                INVOKE_ID_SET.add(invokeId);

                HandlerWrapper wrapper = new HandlerWrapper(invokeId, messageClazz, method, handlerObj, asycRequest != null, fixedRequest != null, noAuthRequest != null);
                mappingList.add(wrapper);
            }

            if (!CollectionUtils.isEmpty(mappingList)) {
                for (HandlerWrapper handlerWrapper : mappingList) {
                    handlerMap.put(handlerWrapper.getInvokerId(), handlerWrapper);
                    if (LOGGER.isInfoEnabled()) {
                        LOGGER.info("Message handler registration succeeded, invokeId:{}, messageClaz:{}, method:{}",
                                handlerWrapper.getInvokerId(), handlerWrapper.getMessageClazz(), handlerWrapper.getMethod().getName());
                    }
                }
            }
        }

    }

    public static HandlerWrapper getHandlerWrapper(long invokeId) {
        return handlerMap.get(invokeId);
    }

}

